package com.ysstech.system.service.impl;

import java.time.LocalDateTime;
import java.util.*;

import javax.servlet.http.HttpServletResponse;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ysstech.common.cache.RedisCacheManager;
import com.ysstech.common.encryption.SM3Utils;
import com.ysstech.common.entity.*;
import com.ysstech.common.enums.YssEnum;
import com.ysstech.common.service.DicvalueService;
import com.ysstech.common.util.resultutil.RestResult;
import com.ysstech.common.util.resultutil.ResultGenerator;
import com.ysstech.common.exception.BusinessException;
import com.ysstech.common.util.DateUtil;
import com.ysstech.common.util.TokenUtil;
import com.ysstech.common.util.fileutil.FileWriterUtil;
import com.ysstech.system.entity.UserRole;
import com.ysstech.system.enums.SystemEnum;
import com.ysstech.system.mapper.RoleMapper;
import com.ysstech.system.mapper.UserMapper;
import com.ysstech.system.service.OrganizationService;
import com.ysstech.system.service.UserRoleService;
import com.ysstech.system.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

/**
 * <p>
 * 用户服务实现类
 * </p>
 *
 * @author lishuangliang
 * @since 2020-09-11
 */
@Service("userService")
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private RoleMapper roleMapper;
    @Autowired
    private OrganizationService organizationService;
    @Autowired
    private RedisCacheManager redisCacheManager;
    @Autowired
    private UserRoleService userRoleService;
    @Autowired
    private DicvalueService dicvalueService;

    /**
     * 用户登录方法
     *
     * @param user
     * @return
     * @throws Exception
     */
    public User login(User user) throws Exception {
        if (null == user || StringUtils.isBlank(user.getLoginId())) {
            throw new BusinessException("获取用户名失败，请稍后重试！");
        }
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("LOGIN_ID", user.getLoginId());
        User queryuser = this.getOne(wrapper);
        if (null == queryuser) {
            throw new BusinessException("没有获取到用户信息，请先注册用户！");
        }
        //用户名密码正确后判断是否被锁定 判断锁定是不是超过24小时
        if (1 == queryuser.getIsLock()
                && LocalDateTime.now().compareTo(queryuser.getLockTime()) < Integer.valueOf(SystemEnum.USER_LOCKTIME.getCode())) {
            throw new BusinessException("对不起，你的账号被锁定，请24小时后登录！");
        }
        if (StringUtils.isNotBlank(user.getPassword()) && queryuser.getPassword().equals(user.getPassword())) {
            loginSuccess(queryuser);//登陆成功后的操作
        } else {
            loginError(queryuser);//密码输入错误后的操作
        }
        return queryuser;
    }

    /**
     * 登陆成功后的操作方法
     *
     * @param queryuser
     * @return
     */
    private void loginSuccess(User queryuser) throws Exception {
        //查询用户的基本信息
        if (StringUtils.isBlank(queryuser.getId())) {
            throw new BusinessException("获取用户信息失败，请稍后重试！");
        }
        if (null != queryuser.getStatus() && (queryuser.getStatus() == 2 || 3 == queryuser.getStatus() || 4 == queryuser.getStatus())) {
            throw new BusinessException("对不起，你的信息还未审核通过，请联系管理员！");
        }
        if (null != queryuser.getStatus() && 5 == queryuser.getStatus()) {
            throw new BusinessException("对不起，你的信息已被注销，请联系管理员！");
        }
        //登陆成功之后修改用户登陆信息
        //修改用户登录信息
        log.info("用户登陆成功，更新登陆时间信息");
        queryuser.setErrorTimes(0);
        queryuser.setIsLock(2);//修改是否锁定状态
        // 保存最后更新时间
        queryuser.setLastLoginTime(LocalDateTime.now());
        this.updateById(queryuser);
        List<Role> list = roleMapper.selectRolesByUserId(queryuser.getId());
        queryuser.setRoles(list);
        queryuser.setOrganization(organizationService.getById(queryuser.getOrgId()));
        String pagetoken = TokenUtil.token(queryuser.getId());
        queryuser.setToken(pagetoken);
        redisCacheManager.putCache(pagetoken, JSONObject.toJSONString(queryuser), Integer.valueOf(YssEnum.YSS_REDIS_EXPIRATION.getCode()));
        //登录认证,认证token
        Subject subject = SecurityUtils.getSubject();
        subject.getSession().setTimeout(-1000L);
        UsernamePasswordToken token = new UsernamePasswordToken(pagetoken, queryuser.getId());
        subject.login(token);
    }

    /**
     * 登陆失败的操作方法
     *
     * @param queryuser
     */
    private void loginError(User queryuser) throws Exception {
        //增加错误密码次数
        int errorTimes = 0;
        if (null == queryuser.getErrorTimes()) {
            throw new BusinessException("对不起，密码输入错误后，请重新输入！");
        }
        if (errorTimes >= Integer.valueOf(SystemEnum.ERROR_TIMES.getCode())) {
            //修改认证信息表锁定该用户
            queryuser.setErrorTimes(0);
            queryuser.setIsLock(1); // 锁定
            queryuser.setLockTime(LocalDateTime.now());//用户锁定时间
            //保存最后更新时间
            queryuser.setLastLoginTime(LocalDateTime.now());
            this.updateById(queryuser);
            log.info("密码错误次数超过限额，修改用户为锁定状态");
            throw new BusinessException("密码输入错误" + SystemEnum.ERROR_TIMES.getCode() + "次，你的账号被锁定，请24小时后登录！");
        } else {
            errorTimes = queryuser.getErrorTimes() + 1;
            queryuser.setErrorTimes(errorTimes);
            this.updateById(queryuser);
            log.info("密码错误增加一次错误次数");
            throw new BusinessException("密码错误" + errorTimes + "次，你今天还有" + (Integer.valueOf(SystemEnum.ERROR_TIMES.getCode()) - errorTimes) + "次机会！");
        }
    }

    /**
     * 用户列表信息查询
     *
     * @param user
     * @param pageBean
     * @return
     * @throws Exception
     */
    public PageBean<User> queryAllUsers(User user, PageBean pageBean) throws Exception {
        QueryWrapper<User> wrapper = this.getQueryWrapper(user);
        IPage pageUser = this.page(new Page<>(pageBean.getPageNum(), pageBean.getPageSize()), wrapper);
        this.switchedListUser(pageUser.getRecords());
        return new PageBean<>(pageUser);
    }


    /**
     * 保存用户的角色信息方法
     *
     * @param list
     * @return
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class)
    public void batchInserUserRole(List<UserRole> list) throws Exception {
        //先删除用户的角色信息，在保存用户的角色信息
        if (list == null || list.size() == 0) {
            throw new BusinessException("获取用户角色信息出错，请稍后重试！");
        }
        for (UserRole userRole : list) {
            userRole.setAddTime(LocalDateTime.now());
        }
        QueryWrapper<UserRole> wrapper = new QueryWrapper<>();
        wrapper.eq("USER_ID", list.get(0).getUserId());
        userRoleService.remove(wrapper);
        //在批量现在用户的角色信息
        userRoleService.saveBatch(list);
    }


    /**
     * 批量删除用户信息
     *
     * @param ids
     * @return
     * @throws Exception
     */
    @Transactional(rollbackFor = Exception.class)
    public void batchDeleteUser(List<String> ids) throws Exception {
        if (ids == null || ids.size() == 0) {
            throw new BusinessException("获取删除信息失败，请稍后重试！");
        }
        User userLoginInfo = redisCacheManager.getLoginUserInfo();
        if (ids.contains(userLoginInfo.getId())) {
            throw new BusinessException("不能删除自己的用户信息，请稍后重试！");
        }
        //在批量删除用户登录表信息 修改用户的状态
        this.removeByIds(ids);
        //批量删除用户对应角色关系
        QueryWrapper<UserRole> wrapper = new QueryWrapper<>();
        wrapper.in("USER_ID", ids);
        userRoleService.remove(wrapper);
    }

    /**
     * 锁定或者解锁用户
     *
     * @param list
     * @return
     */
    public void updateIsLock(List<User> list) throws Exception {
        if (CollectionUtils.isEmpty(list)) {
            throw new BusinessException("没有获取到选择的用户信息，请稍后重试！");
        }
        User userLoginInfo = redisCacheManager.getLoginUserInfo();
        for (User info : list) {
            if (info.getId().equals(userLoginInfo.getId())) {
                throw new BusinessException("不能锁定或者解锁自己的信息！");
            }
        }
        this.updateBatchById(list);
    }

    /**
     * 重置用户密码
     *
     * @param list
     * @return
     * @throws Exception
     */
    public void resetPassword(List<String> list) throws Exception {
        if (CollectionUtils.isEmpty(list)) {
            throw new BusinessException("获取需要重置密码的用户信息失败，请稍后重试！");
        }
        List<User> listuser = new ArrayList<>();
        for (String id : list) {
            if (StringUtils.isBlank(id)) {
                continue;
            }
            User user = new User();
            user.setId(id);
            user.setPassword(SM3Utils.sm3encrypt(SystemEnum.RESET_PASSWORD.getCode()));
            user.setIsFirstLogin(1);
            user.setAddTime(LocalDateTime.now());
            listuser.add(user);
        }
        if (CollectionUtils.isEmpty(listuser)) {
            throw new BusinessException("获取需要重置密码的用户信息失败，请稍后重试！");
        }
        this.updateBatchById(listuser);
    }


    /**
     * 更攺用户密码
     *
     * @param oldPassword    旧密码
     * @param newPassword    新密码
     * @param affirmPassword 重复新密码
     * @throws Exception
     */
    public void updateUserPassword(String oldPassword, String newPassword, String affirmPassword) throws Exception {
        User userinfo = redisCacheManager.getLoginUserInfo();
        if (null == userinfo) {
            throw new BusinessException("获取用户信息失败，请重新输入！");
        }
        if (StringUtils.isBlank(oldPassword) || StringUtils.isBlank(newPassword) || StringUtils.isBlank(affirmPassword)) {
            throw new BusinessException("获取输入密码信息失败，请重新输入！");
        }
        if (!newPassword.equals(affirmPassword)) {
            throw new BusinessException("两次输入的新密码不一致，请重新输入！");
        }
        userinfo.setIsFirstLogin(2);
        if (!userinfo.getPassword().equals(oldPassword)) {
            throw new BusinessException("输入原密码不正确，请重新输入！");
        }
        userinfo.setPassword(newPassword);
        this.updateById(userinfo);
    }


    /**
     * 导出用户信息
     *
     * @param response
     * @param user
     * @throws Exception
     */
    @Override
    public void userexport(HttpServletResponse response, User user) throws Exception {
        QueryWrapper<User> wrapper = this.getQueryWrapper(user);
        List<User> list = this.list(wrapper);
        this.switchedListUser(list);
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        for (User uservo : list) {//循环所有用户信息
            String sex = "女";
            if (1 == uservo.getSex()) {
                sex = "男";
            }
            uservo.setStrSex(sex);
        }
        List<SheetDTO> sheetList = new ArrayList<>();// 创建sheet集合，用来存储sheet
        Map<String, String> fieldAndAlias = new LinkedHashMap<>();
        // 设置只导出有别名的字段
        fieldAndAlias.put("loginId", "登录名");
        fieldAndAlias.put("name", "用户姓名");
        fieldAndAlias.put("strSex", "性别");
        fieldAndAlias.put("phone", "手机号码");
        fieldAndAlias.put("orgId", "所属机构");
        fieldAndAlias.put("lastModify", "最后更新人");
        fieldAndAlias.put("LocalDateTime", "最后更新时间");
        SheetDTO sheet = new SheetDTO();
        sheet.setFieldAndAlias(fieldAndAlias);
        sheet.setSheetName("用户信息");
        sheet.setCollection(list);
        sheetList.add(sheet);
        FileWriterUtil.exportExcel(response, sheetList, "用户信息_", YssEnum.YSS_XLSX.getCode());
    }

    /**
     * 类型，机构等中文的转换
     *
     * @param list
     * @return
     * @throws Exception
     */
    private List<User> switchedListUser(List<User> list) throws Exception {
        Map<String, Organization> map = organizationService.getCacheOrganizationMap();
        Map<String, String> mapUserStatus = dicvalueService.getCacheDicValueMap(SystemEnum.SYS_STATUS.getCode());
        for (User user : list) {
            if (null == user) {
                continue;
            }
            if (null != map && map.containsKey(user.getOrgId())) {
                Organization organization = map.get(user.getOrgId());
                if (null == organization) {
                    continue;
                }
                user.setStrOrgName(organization.getFullName());
            }
            if (null != mapUserStatus && mapUserStatus.containsKey(String.valueOf(user.getStatus()))) {
                user.setStrStatus(mapUserStatus.get(String.valueOf(user.getStatus())));
            }
        }
        return list;
    }


    /**
     * 查询条件
     *
     * @param user
     * @return
     * @throws Exception
     */
    private QueryWrapper<User> getQueryWrapper(User user) throws Exception {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.orderByDesc("LAST_MODIFY_TIME");
        if (null == user) {
            return wrapper;
        }
        //查询当前登录人所在机构以及下级机构用户信息
        List<String> listOrganization = organizationService.queryOrganizationIds(redisCacheManager.getLoginUserInfo().getOrgId());
        wrapper.in("ORG_ID", listOrganization);
        if (StringUtils.isNotBlank(user.getStrOrgName())) {
            List<String> list = JSONArray.parseArray(user.getStrOrgName(), String.class);
            if (!CollectionUtils.isEmpty(list)) {
                wrapper.in("ORG_ID", list);
            }
        }
        if (StringUtils.isNotBlank(user.getName())) {
            wrapper.like("NAME", user.getName());
        }
        if (StringUtils.isNotBlank(user.getLoginId())) {
            wrapper.like("LOGIN_ID", user.getLoginId());
        }
        if (StringUtils.isNotBlank(user.getStrStatus())) {
            List<Integer> list = JSONArray.parseArray(user.getStrStatus(), Integer.class);
            if (!CollectionUtils.isEmpty(list) && null != list.get(0)) {
                wrapper.in("STATUS", list);
            }
        }
        return wrapper;
    }


    public RestResult scheduleTest(String code) throws Exception {
        log.info("进入定时任务接口" + code);
        return ResultGenerator.getSuccessResult("定时任务启动成功！");
    }
}
